﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Threading;


namespace KnightShortestPath
{

    /// <summary>
    /// Interaction logic for ChessBoard.xaml
    /// </summary>
    public partial class ChessBoard : Window
    {
        /// <summary>
        /// Matrix of instances of the <see cref="System.Windows.Controls.Grid"/> class that represent each of the cells of a chessboard
        /// </summary>
        ///         
        private Grid[] cell = new Grid[64];

        /// <summary>
        /// Instance of the <see cref="KnightShortestPath.GameController"/> class that processes data and calculates the possible movements of the knight
        /// </summary>
        private GameController game;

        /// <summary>
        /// List of instances of the <see cref="KnightShortestPath.Node"/> class which contains all shortest paths found
        /// </summary>
        private List<Node> route;


        /// <summary>
        /// Creates a new instance of the <see cref="KnightShortestPath.ChessBoard"/> class.
        /// </summary>
        public ChessBoard()
        {
            InitializeComponent();                         

            // Instanciates the 64 cell of the chessboard
            for (int i = 0; i < 64; i++)
            {
                SolidColorBrush clearBrush = new SolidColorBrush(Colors.White);
                SolidColorBrush darkBrush = new SolidColorBrush(Colors.Black);
                cell[i] = new Grid();
                cell[i].Background = (i + i / 8) % 2 == 0 ? clearBrush : darkBrush;
                cell[i].AllowDrop = true;
                cell[i].DragEnter += new DragEventHandler(this.Cells_DragEnter);
                board.Children.Add(cell[i]);
            }

            // Initializes the default positions of Knight and Queen
            actions.Children.Remove(knightsRect);
            cell[0].Children.Add(knightsRect);
            actions.Children.Remove(queensRect);
            cell[63].Children.Add(queensRect);
        }

        /// <summary>
        /// Looks for the position of a specific rectangle which represents knight's or queen's position
        /// </summary>
        /// <param name="rect">
        /// Rect: instance of the <see cref="System.Windows.Controls.Shapes.Rectangle"/> class that represents 
        /// a specific cell in the chessboard
        /// </param>
        /// <returns>
        /// Returns an instance of the <see cref="KnightShortestPath.Node"/> class that represents a position 
        /// on the chessboard
        /// </returns>
        private Node FindPosition(Rectangle rect)
        {
            for (int i = 0; i < 64; i++)
            {
                // looks for the cell that contains the rect, then, creates a new instance of Node by using
                // the row and column according to the cell
                if (cell[i].Children.Contains(rect))
                {
                    int row = i / 8;
                    int col = i % 8;
                    return new Node((byte)row, (byte)col);
                }
            }
            return null;
        }

        /// <summary>
        /// Implements the action that knight's rectangle and queen's rectangle delegates have 
        /// to perform on <see cref="System.Windows.UIElement.MouseLeftButtonDown"/>
        /// </summary>
        /// <param name="sender">
        /// Sender: instance of the <see cref="System.Object"/> class
        /// </param>
        /// <param name="e">
        /// e: instance of the <see cref="System.Windows.Input.MouseButtonEventArgs"/> class
        /// </param>
        private void rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Rectangle rect = (Rectangle)e.Source;
            DataObject data = new DataObject(typeof(Rectangle), rect);
            DragDrop.DoDragDrop(rect, data, DragDropEffects.Move);
        }

        /// <summary>
        /// Implements the action that cells' delegates have to perform on
        /// <see cref="System.Windows.UIElement.DragEnter"/>
        /// </summary>
        /// <param name="sender">
        /// Sender: instance of the <see cref="System.Object"/> class
        /// </param>
        /// <param name="e">
        /// e: instance of the <see cref="System.Windows.DragEventArgs"/> class
        /// </param>
        private void Cells_DragEnter(object sender, DragEventArgs e)
        {
            Rectangle rect = (Rectangle)e.Data.GetData(typeof(Rectangle));
            Panel container = (Panel)rect.Parent;
            container.Children.Remove(rect);
            Grid cell = (Grid)sender;
            cell.Children.Add(rect);
        }

        /// <summary>
        /// Implements the action that btnProcessing delegate has to perform on
        /// <see cref="System.Windows.UIElement.Click"/>
        /// </summary>
        /// <param name="sender">
        /// Sender: instance of the <see cref="System.Object"/> class
        /// </param>
        /// <param name="e">
        /// e: instance of the <see cref="System.Windows.RoutedEventArgs"/> class
        /// </param>
        private void BtnProcessing_Click(object sender, RoutedEventArgs e)
        {
            // Turns the processing mode off and clears the routes list
            SetProcessingMode(true);
            Thread.Sleep(1000);
            this.routesList.Items.Clear();
            
            // Obtains knight's and queen's position to start the processing
            Node knightPosition = this.FindPosition(knightsRect);
            Node finalPosition = this.FindPosition(queensRect);
            game = new GameController(knightPosition,finalPosition);
            game.startProcessing();

            // Enumarates the number of routes found and update labels
            int routesFound = game.Paths.GetLength(0);
            for (int i = 0; i < routesFound; i++ )
            {
                routesList.Items.Add("Route "+(i+1));
            }
            this.lblMovements.Content = game.NumberOfMovements.ToString();
            this.lblSteps.Content = game.StepsInShortestPath.ToString();

            // Turns the processing mode on again
            Thread.Sleep(2500);
            SetProcessingMode(false);
        }

        /// <summary>
        /// Implements the action that btnRoute's delegate has to perform on
        /// <see cref="System.Windows.UIElement.Click"/>
        /// </summary>
        /// <param name="sender">
        /// Sender: instance of the <see cref="System.Object"/> class
        /// </param>
        /// <param name="e">
        /// e: instance of the <see cref="System.Windows.RoutedEventArgs"/> class
        /// </param>
        private void BtnRoute_Click(object sender, RoutedEventArgs e)
        {
            int index = this.routesList.SelectedIndex;
            if (index >= 0) {
                this.route = game.Paths[index];
                route.Reverse();
                Thread threadTrace = new Thread(new ThreadStart(this.TraceSteps));
                threadTrace.Start();                
            }
        }

        /// <summary>
        /// Updates the position of the knight on the chessboard window in a safe mode
        /// by using <see cref="System.Object.Dispatcher.Invoke"/>
        /// </summary>
        private void TraceSteps()
        {
            foreach (Node step in this.route)
            {
                this.Dispatcher.Invoke(new Action(
                    delegate() {
                        Panel container = (Panel)knightsRect.Parent;
                        container.Children.Remove(knightsRect);
                        container.UpdateLayout();
                        int nextPosition = step.Row * 8 + step.Col;
                        cell[nextPosition].Children.Add(knightsRect);
                        cell[nextPosition].UpdateLayout();
                        Thread.Sleep(2500);
                    }
                ),System.Windows.Threading.DispatcherPriority.Render);
            }
        }

        /// <summary>
        /// Sets the processing mode on or off, that allows user to operate 
        /// "Start Processing" button
        /// </summary>
        /// <param name="isInProcess">
        /// True to turn the processing mode off, otherwise False
        /// </param>
        private void SetProcessingMode(Boolean isInProcess)
        {
            Thread processing;
            if (isInProcess) {
                processing = new Thread(new ThreadStart(this.ProcessingStarted));
            }
            else {
                processing = new Thread(new ThreadStart(this.ProcessingOver));
            }
            processing.Start();
        }

        /// <summary>
        /// Turns the processing mode off by disabling and hiding "Start Processing" button. 
        /// This action is performed in a safe modeby using <see cref="System.Object.Dispatcher.Invoke"/>
        /// </summary>
        private void ProcessingStarted(){
            this.Dispatcher.Invoke(new Action(
                delegate()
                {
                    this.btnProcessing.IsEnabled = false;
                    this.lblProcessing.Visibility = System.Windows.Visibility.Visible;
                    this.btnProcessing.Visibility = System.Windows.Visibility.Hidden;
                    this.UpdateLayout();
                }
            ), System.Windows.Threading.DispatcherPriority.Render);
        }

        /// <summary>
        /// Turns the processing mode on by enabling and showing "Start Processing" button. 
        /// This action is performed in a safe modeby using <see cref="System.Object.Dispatcher.Invoke"/>
        /// </summary>
        private void ProcessingOver(){
            this.Dispatcher.Invoke(new Action(
                delegate()
                {
                    this.btnProcessing.IsEnabled = true;
                    this.lblProcessing.Visibility = System.Windows.Visibility.Hidden;
                    this.btnProcessing.Visibility = System.Windows.Visibility.Visible;
                    this.UpdateLayout();                    
                }
            ), System.Windows.Threading.DispatcherPriority.Render);
        }

    }
}
